package com.mioto.ld.communication.mqtt.protocol;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.mioto.ld.communication.mqtt.protocol.cache.ReportAttributeCache;
import com.mioto.ld.communication.mqtt.protocol.model.AlertLogCondition;
import com.mioto.ld.communication.mqtt.protocol.model.DeviceMTypeEnum;
import com.mioto.ld.communication.mqtt.protocol.model.Message;
import com.mioto.ld.communication.mqtt.protocol.model.device.DeviceFaultReportPayload;
import com.mioto.ld.module.alert.model.AlertTrigger;
import com.mioto.ld.module.alert.model.LinkageTrigger;
import com.mioto.ld.module.alert.model.TriggerTargetType;
import com.mioto.ld.module.device.model.Device;
import com.mioto.ld.module.device.model.DeviceFaultLog;
import com.mioto.ld.module.device.service.IDeviceFaultLogService;
import com.mioto.ld.module.product.service.IProductEventService;
import com.mioto.ld.utils.SpringBeanUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * @author mioto-qinxj
 * @date 2024/6/21
 * @desc
 */
@Slf4j
public class FaultReportTriggerHandler extends TriggerHandler{
    private IDeviceFaultLogService deviceFaultLogService;
    public FaultReportTriggerHandler(){
        deviceFaultLogService = SpringBeanUtil.getBean(IDeviceFaultLogService.class);
    }

    @Override
    protected void triggerProcess(Message message, Device device) {
        log.info("---------------告警触发器条件判断---------------");
        DeviceFaultReportPayload payload = (DeviceFaultReportPayload) message.getMessageBody().getPayload();
        Map<Integer,String> customEventMap = SpringBeanUtil.getBean(IProductEventService.class).
                findEventByDeviceIdAndIdentitys(device.getId(),payload.getFaults());
        if (CollUtil.isNotEmpty(customEventMap)) {
            List<AlertTrigger> alertTriggers = alertTriggerService.findEventTriggersByDeviceId(device.getId());
            Map<String,Object> reportAttr = ReportAttributeCache.getAttributes(device.getSn());
            Map<Integer,String> customAttr  = findDeviceAttributes(device.getProductId(), reportAttr);
            boolean compareAttr = CollUtil.isNotEmpty(customAttr);
            if (message.getMtype().equals(DeviceMTypeEnum.DEVICE_FAULT_REPORT.getMtype())) {
                //故障上报
                //记录设备故障信息
                saveDeviceFaultLog(device.getId(),payload.getReportDate(),customEventMap);
                //触发条件判断，如果有条件满足，保存告警记录,并执行告警通知
                Set<Integer> alertIds = faultReportCompare(alertTriggers, compareAttr, reportAttr, customAttr, customEventMap);
                if (!alertIds.isEmpty()){
                    AlertLogCondition alertLogCondition = AlertLogCondition.builder()
                            .reportAttribute(reportAttr)
                            .reportFault(customEventMap).build();
                    Integer alertId = CollUtil.get(alertIds,0);
                    boolean saveResult = saveAlertLog(device.getId(),alertId,payload.getReportDate(),alertLogCondition.toJsonStr());
                    if (saveResult) {
                        deviceNotifyProcess.executeAlertNotify(alertId, device);
                    }
                }

                linkageTriggerProcess(device,compareAttr,reportAttr,customAttr,customEventMap);
            }else if (message.getMtype().equals(DeviceMTypeEnum.DEVICE_FAULT_RECOVERY_REPORT.getMtype())){
                //故障恢复上报
                //更新设备故障状态
                Integer[] targetIds = customEventMap.keySet().toArray(new Integer[0]);
                deviceFaultLogService.updateRecoveryDate(device.getId(), targetIds, payload.getReportDate());
                //触发条件判断，如果有条件满足，更新故障恢复记录
                Set<Integer> alertIds = faultRecoveryReportCompare(alertTriggers, compareAttr, reportAttr, customAttr, customEventMap);
                if (!alertIds.isEmpty()){
                    //更新告警恢复记录
                    AlertLogCondition alertLogCondition = AlertLogCondition.builder()
                            .reportAttribute(reportAttr)
                            .reportFault(customEventMap).build();
                    deviceAlertLogService.updateRecoveryData(device.getId(),alertIds
                                    ,payload.getReportDate(),alertLogCondition.toJsonStr());
                }
            }

        }
    }

    private Set<Integer> faultRecoveryReportCompare(List<AlertTrigger> alertTriggers,boolean compareAttr, Map<String, Object> reportAttr, Map<Integer, String> customAttr,Map<Integer,String> customEventMap){
        Integer alertId = 0;
        Set<Integer> alertIds = new HashSet<>(2);
        boolean compareResult = false;
        for (AlertTrigger alertTrigger : alertTriggers) {
            //同一个告警配置，如果有一个条件失败，直接判断下一个告警配置
            if (ObjectUtil.equal(alertId,alertTrigger.getAlertId()) && !compareResult){
                alertIds.remove(alertId);
                continue;
            }
            if (ObjectUtil.equal(alertTrigger.getTriggerTarget(), TriggerTargetType.TRIGGER_TARGET_TYPE_EVENT)) {
                compareResult = customEventMap.containsKey(alertTrigger.getTargetId()) ? true : false;
            } else if (ObjectUtil.equal(alertTrigger.getTriggerTarget(), TriggerTargetType.TRIGGER_TARGET_TYPE_DEVICE)){
                if (compareAttr){
                    log.info("start compare attribute trigger conditions - {}",alertTrigger.getTriggerCondition());
                    compareResult =!checkAttributeTriggerConditions(alertTrigger.getTriggerCondition()
                            ,alertTrigger.getTargetId(),customAttr,reportAttr);
                }else {
                    compareResult = false;
                }
            }
            alertId = alertTrigger.getAlertId();
            if (compareResult){
                alertIds.add(alertId);
            }
        }
        return alertIds;
    }

    private Set<Integer> faultReportCompare( List<AlertTrigger> alertTriggers,boolean compareAttr, Map<String, Object> reportAttr, Map<Integer, String> customAttr,Map<Integer,String> customEventMap){
        boolean compareResult = false;
        Integer alertId = 0;
        Set<Integer> alertIds = new HashSet<>(2);
        for (AlertTrigger alertTrigger : alertTriggers) {
            //同一个告警配置，如果有一个条件失败，直接判断下一个告警配置
            if (ObjectUtil.equal(alertId,alertTrigger.getAlertId()) && !compareResult){
                alertIds.remove(alertId);
                continue;
            }else if (!ObjectUtil.equal(alertId,alertTrigger.getAlertId()) && compareResult){
                break;
            }
            compareResult = compareTriggerCondition(alertTrigger.getTargetId(), alertTrigger.getTriggerTarget(),
                    alertTrigger.getTriggerCondition(), compareAttr, reportAttr, customAttr, customEventMap);
            alertId = alertTrigger.getAlertId();
            if (compareResult){
                alertIds.add(alertId);
            }
        }
        return alertIds;
    }

    protected void linkageTriggerProcess(Device device,boolean compareAttr, Map<String, Object> reportAttr, Map<Integer, String> customAttr,Map<Integer,String> customEventMap) {
        log.info("---------------联动触发器条件判断---------------");
        int linkageId = 0;
        boolean compareResult = false;
        List<LinkageTrigger> linkageTriggers = linkageTriggerService.findEventTriggersByDeviceId(device.getId());
        for (LinkageTrigger linkageTrigger : linkageTriggers) {
            //同一个配置，如果有一个条件失败，直接判断下一个配置
            if (linkageId == linkageTrigger.getLinkageId() && !compareResult){
                continue;
            }else if (linkageId != linkageTrigger.getLinkageId() && compareResult){
                break;
            }
            compareResult = compareTriggerCondition(linkageTrigger.getTargetId(), linkageTrigger.getTriggerTarget(),
                    linkageTrigger.getTriggerCondition(), compareAttr, reportAttr, customAttr, customEventMap);
            linkageId = linkageTrigger.getLinkageId();
        }
        //如果条件全部满足，执行动作
        if (compareResult){
            executeLinkageFunction(device,linkageId);
        }
    }

    private boolean compareTriggerCondition(Integer targetId,Integer triggerTarget,String triggerCondition,
                                            boolean compareAttr, Map<String, Object> reportAttr, Map<Integer, String> customAttr,
                                            Map<Integer,String> customEventMap){
        if (ObjectUtil.equal(triggerTarget, TriggerTargetType.TRIGGER_TARGET_TYPE_EVENT)) {
            log.info("start compare event,report event- {},trigger event - {}",
                    JSONUtil.parse(customEventMap),targetId);
            return customEventMap.containsKey(targetId) ? true : false;
        } else if (ObjectUtil.equal(triggerTarget, TriggerTargetType.TRIGGER_TARGET_TYPE_DEVICE)){
            if (compareAttr){
                log.info("start compare attribute trigger conditions - {}", triggerCondition);
                return checkAttributeTriggerConditions(triggerCondition
                        ,targetId,customAttr,reportAttr);
            }else {
                return false;
            }
        }
        return false;
    }

    /**
     * 记录设备故障信息
     * @param deviceId
     * @param reportTime
     * @param sysEventMap
     */
    private void saveDeviceFaultLog(int deviceId, Date reportTime, Map<Integer,String> sysEventMap){
        Iterator<Integer> iterator = sysEventMap.keySet().iterator();
        DeviceFaultLog deviceFaultLog;
        int faultType;
        int result;
        while (iterator.hasNext()){
            faultType = iterator.next();
            result = deviceFaultLogService.existFault(deviceId,faultType);
            if (result > 0){
                continue;
            }
            //如果设备不存在未恢复的故障，保存该故障信息
            deviceFaultLog = new DeviceFaultLog();
            deviceFaultLog.setDeviceId(deviceId);
            deviceFaultLog.setFaultType(faultType);
            deviceFaultLog.setFaultStatus(0);
            deviceFaultLog.setReportDate(reportTime);
            deviceFaultLogService.insert(deviceFaultLog);
        }
    }
}
